﻿using System;
using System.Collections.Generic;
using System.Diagnostics.Contracts;
using Adalbertus.BudgetPlanner.Domain;
using Adalbertus.BudgetPlanner.Domain.Repositories;

namespace Adalbertus.BudgetPlanner.Services
{
    public class BaseService<TDomain> : IBaseService<TDomain>
    {
        private readonly IRepositoryFactory _repositoryFactory;
        protected IRepositoryFactory RepositoryFactory
        {
            get
            {
                Contract.Ensures(Contract.Result<IRepositoryFactory>() != null);
                return _repositoryFactory;
            }
        }

        private readonly IUnitOfWorkProvider _unitOfWorkProvider;
        protected IUnitOfWorkProvider UnitOfWorkProvider
        {
            get
            {
                Contract.Ensures(Contract.Result<IUnitOfWorkProvider>() != null);
                return _unitOfWorkProvider;
            }
        }

        public BaseService(IRepositoryFactory repositoryFactory, IUnitOfWorkProvider unitOfWorkProvider)
        {
            Contract.Requires<ArgumentNullException>(repositoryFactory != null);
            Contract.Requires<ArgumentNullException>(unitOfWorkProvider != null);

            _repositoryFactory = repositoryFactory;
            _unitOfWorkProvider = unitOfWorkProvider;
        }

        public virtual void Save(TDomain domain)
        {
            using (IUnitOfWork unitOfWork = UnitOfWorkProvider.OpenUnitOfWork())
            {
                IRepository<TDomain> repository = RepositoryFactory.CreateRepository<TDomain>(unitOfWork);
                repository.Save(domain);
                unitOfWork.Commit();
            }
        }

        public void Save(IEnumerable<TDomain> domains)
        {
            using (IUnitOfWork unitOfWork = UnitOfWorkProvider.OpenUnitOfWork())
            {
                IRepository<TDomain> repository = RepositoryFactory.CreateRepository<TDomain>(unitOfWork);
                repository.Save(domains);
                unitOfWork.Commit();
            }
        }

        public virtual void Remove(TDomain domain)
        {
            using (IUnitOfWork unitOfWork = UnitOfWorkProvider.OpenUnitOfWork())
            {
                IRepository<TDomain> repository = RepositoryFactory.CreateRepository<TDomain>(unitOfWork);
                repository.Remove(domain);
                unitOfWork.Commit();
            }
        }

        public virtual TDomain GetById(int id)
        {
            using (IUnitOfWork unitOfWork = UnitOfWorkProvider.OpenUnitOfWork())
            {
                IRepository<TDomain> repository = RepositoryFactory.CreateRepository<TDomain>(unitOfWork);
                TDomain domain = repository.GetById(id);
                return domain;
            }
        }

        public virtual IEnumerable<TDomain> GetAll()
        {
            using (IUnitOfWork unitOfWork = UnitOfWorkProvider.OpenUnitOfWork())
            {
                IRepository<TDomain> repository = RepositoryFactory.CreateRepository<TDomain>(unitOfWork);
                IEnumerable<TDomain> domains = repository.GetAll();
                return domains;  
            }
        }
    }
}
